package com.syh.jn.scm.domain.upstream.entity.logistics;

import cn.redscarf.commons.util.bean.ExtBeanUtils;
import cn.redscarf.commons.util.exception.IllegalParameterException;
import cn.redscarf.commons.util.other.ExtAssert;
import cn.redscarf.commons.util.other.ExtStringUtils;
import cn.redscarf.commons.spring.SpringContextUtils;
import cn.redscarf.commons.util.validator.ValidationUtils;
import com.syh.core.ddd.domain.AbstractSimpleAggregate;
import com.syh.core.ddd.domain.CodeExpEnum;
import com.syh.core.ddd.domain.CodeGenerateUtil;
import com.syh.jn.scm.domain.upstream.entity.sale.UpSaleOrder;
import com.syh.jn.scm.domain.upstream.entity.sale.UpSaleReturnOrder;
import com.syh.jn.scm.domain.upstream.repository.logistics.UpLogisticsBillRepository;
import com.syh.jn.scm.domain.upstream.repository.sale.UpSaleOrderReadRepository;
import com.syh.jn.scm.domain.upstream.repository.sale.UpSaleReturnOrderReadRepository;
import com.syh.jn.scm.dto.upstream.logistics.UpLogisticsBillMto;
import com.syh.jn.scm.dto.upstream.logistics.UpLogisticsBillSto;
import com.syh.jn.scm.exception.BusinessException;
import com.syh.jn.scm.service.api.supplier.logistics.enums.LogisticsBillEnums;
import com.syh.jn.scm.service.api.upstream.logistics.enums.UpLogisticsBillEnum;
import com.syh.jn.scm.service.api.upstream.sale.enums.UpSaleOrderEnums;
import com.syh.jn.scm.service.api.upstream.sale.enums.UpSaleReturnOrderEnums;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * 上游物流单聚合
 */
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class UpLogisticsBill extends AbstractSimpleAggregate<Long> {
  /**
   * 物流单编号
   */
  private String code;
  /**
   * 对应订单id
   */
  private Long orderId;
  /**
   * 订单类型
   */
  private Short orderType;
  /**
   * 订单编号
   */
  private String orderCode;
  /**
   * 客户类型
   */
  private Short customerType;
  /**
   * 客户id
   */
  private Long customerId;
  /**
   * 物流状态(0：未发车 1:已发车 2：已收货)
   */
  private Short logisticsState;
  /**
   * 物流单物流结算状态(0：未生成对账单 1：已生成对账单 -1：已取消)
   */
  private Short statementState;
  /**
   * 物流类型(1:空运 2:陆运 3:海运)
   */
  private Short logisticsType;
  /**
   * 物流公司id
   */
  private Long logisticsCompanyId;
  /**
   * 航班号
   */
  private String flightNumber;
  /**
   * 海航号
   */
  private String shippingNumber;
  /**
   * 货车车牌号
   */
  private String plateNumber;
  /**
   * 预计到达时间(物流)
   */
  private Long etaDateTimestamp;
  /**
   * 司机姓名
   */
  private String driverName;
  /**
   * 司机联系电话
   */
  private String driverContactPhone;
  /**
   * 发货备注(物流)
   */
  private String deliveryNotes;
  /**
   * 发货地区编码
   */
  private String deliveryDeliAreaCode;
  /**
   * 发货地址省 市 区
   */
  private String deliveryDeliArea;
  /**
   * 发货人详细地址
   */
  private String deliveryDeliAddress;
  /**
   * 发货时间
   */
  private Long deliveryTimestamp;
  /**
   * 上传发货图片URL
   */
  private String deliveryCarImgUrl;
  /**
   * 上传发货图片URL
   */
  private String deliveryContractImgUrl;
  /**
   * 上传收货图片URL
   */
  private String collectContractImgUrl;
  /**
   * 运费承担方(0:买家承担  1:卖家承担费用)
   */
  private Short freightTake;
  /**
   * 运费承担方式(0:预收买家  1:买家到付)
   */
  private Short freightType;
  /**
   * 运费金额
   */
  private BigDecimal totalFreight;
  /**
   * 收货人姓名
   */
  private String deliName;
  /**
   * 收货人联系电话
   */
  private String deliTel;
  /**
   * 要求到货时间
   */
  private Long receiveTimestamp;
  /**
   * 收货地区编码
   */
  private String deliAreaCode;
  /**
   * 收货地址省 市 区
   */
  private String deliArea;
  /**
   * 收货人详细地址
   */
  private String deliAddress;
  /**
   * 入库时间(收货)
   */
  private Long storageTimestamp;
  /**
   * 归属服务商id
   */
  private Long businessEntityId;
  /**
   * 主单id
   */
  private Long subjectBillId;
  /**
   * 副单（直采直销用）
   */
  private UpLogisticsBill secondaryBill;
  /**
   * 发货类型(1:物流发货,2:快递发货,3:其它发货)
   */
  private Short deliveryType;
  /**
   * 快递单号
   */
  private String courierNumber;
  /**
   * items
   */
  private List<UpLogisticsBillItem> items;

  /**
   * 新增物流单
   * @param sto
   */
  public UpLogisticsBill(UpLogisticsBillSto sto) {
    ExtAssert.notNull(sto, "对象为空");
    UpSaleOrderReadRepository upSaleOrderReadRepository = (UpSaleOrderReadRepository) SpringContextUtils.getBean(UpSaleOrderReadRepository.class);
    UpSaleOrder upSaleOrder = upSaleOrderReadRepository.getById(sto.getOrderId());
    if (upSaleOrder != null) {
      if (upSaleOrder.getGeneratedState().equals(UpSaleOrderEnums.GeneratedState.LOGISTICS_WHOLE_GENERATE.getStatus())) {
        throw new BusinessException("销售单" + upSaleOrder.getCode() + "现有物流单已满足请货数量，无法再新建物流单！");
      }
      sto.setOrderCode(upSaleOrder.getCode());
      sto.setCustomerType(UpLogisticsBillEnum.CUSTOMER_TYPE_UPSTREAM.getStatus());
      sto.setCustomerId(upSaleOrder.getUpStreamCompanyId());
      sto.setDeliAddress(upSaleOrder.getDeliAddress());
      sto.setDeliArea(upSaleOrder.getDeliArea());
      sto.setDeliAreaCode(upSaleOrder.getDeliAreaCode());
      sto.setDeliName(upSaleOrder.getDeliName());
      sto.setDeliTel(upSaleOrder.getDeliTel());
      sto.setReceiveTimestamp(upSaleOrder.getDeliveryTimestamp());
    }
    ValidationUtils.validate(sto);
    switch (sto.getDeliveryType().intValue()) {
      //1.是物流发货时
      case 1:
        switch (sto.getLogisticsType().intValue()) {
          case 1:
            if (sto.getDriverContactPhone() == null || sto.getDriverName() == null || sto.getPlateNumber() == null) {
              throw new IllegalParameterException("选择陆运，货车车牌号、司机姓名、司机联系方式不能为空");
            }
            break;
          case 2:
            if (sto.getFlightNumber() == null) {
              throw new IllegalParameterException("选择空运，航班号不能为空");
            }
            break;
          case 3:
            if (sto.getShippingNumber() == null) {
              throw new IllegalParameterException("选择海运，航海号不能为空");
            }
            break;
          default:
            throw new IllegalParameterException("物流类型错误");
        }
        break;
      //2.是快递发货时
      case 2:
        if (ExtStringUtils.isEmpty(sto.getCourierNumber())) {
          throw new BusinessException("快递单号不能为空！");
        }
        if (ExtStringUtils.isBlank(String.valueOf(sto.getFreightTake().intValue()))) {
          throw new BusinessException("运费承担方不能为空！");
        }
        if (sto.getFreightTake().equals(LogisticsBillEnums.FreightTake.BUY.getStatus())) {
          if (sto.getFreightType() == null)
            throw new BusinessException("运费承担方式不能为空！");
        }
        if (sto.getLogisticsType() != null) {
          sto.setLogisticsType(null);
        }
        break;
      //3.是其它发货时
      case 3:
        if (sto.getLogisticsType() != null) {
          sto.setLogisticsType(null);
        }
        if (sto.getFreightTake().equals(LogisticsBillEnums.FreightTake.BUY.getStatus())) {
          if (sto.getFreightType() == null)
            throw new BusinessException("运费承担方式不能为空！");
        }
        break;
      default:
        throw new IllegalParameterException("发货类型错误");
    }
    ExtBeanUtils.copyProperties(sto, this);
    generatorCommonValue();
    ExtAssert.notNull(sto.getItems(), "物流单明细对象为空");
    this.items = new ArrayList<>();
    sto.getItems().stream()
            .filter(item -> item.getQuantity().compareTo(BigDecimal.ZERO) > 0 )
            .forEach(item -> {
              this.items.add(new UpLogisticsBillItem(item));
            });
  }

  /**
   * 修改i物流单
   */
  public void modify(UpLogisticsBillMto mto) {
    ExtAssert.notNull(mto, "对象为空");
    if (this.subjectBillId != null) {
      throw new BusinessException("该物流单为直采直销的销售物流订单，请修改直采直销的采购物流订单");
    }
    ValidationUtils.validate(mto);
    if (!this.getLogisticsState().equals(UpLogisticsBillEnum.DRIVER_UNDELIVERY.getStatus())) {
      throw new BusinessException("订单不是未发货状态，不能修改");
    }
    this.lastUpdateTimeStamp = System.currentTimeMillis();
    ExtBeanUtils.copyProperties(mto, this);
  }

  private void generatorCommonValue() {
    this.code = CodeGenerateUtil.codeGenerate(CodeExpEnum.LOGISTICS_BILL_CODE_EXP.getCodeExp(), this.orderId);
    this.logisticsState = UpLogisticsBillEnum.DRIVER_UNDELIVERY.getStatus();
    initDefault();
  }

  /**
   * 发货
   */
  public void delivery() {
    if (!this.orderType.equals(UpLogisticsBillEnum.ORDER_TYPE_SALE.getStatus())) {
      throw new BusinessException("非我方发货的物流单，发货失败！");
    }
    if (this.logisticsState == null || !this.logisticsState.equals(UpLogisticsBillEnum.DRIVER_UNDELIVERY.getStatus()))
      throw new BusinessException("物流单据状态不是未发车，确认发货失败！");
    this.juStatement(0);
    this.logisticsState = UpLogisticsBillEnum.DRIVER_DELIVEED.getStatus();
    UpLogisticsBillRepository upLogisticsBillRepository = (UpLogisticsBillRepository) SpringContextUtils.getBean(UpLogisticsBillRepository.class);
    this.setItems(upLogisticsBillRepository.getItemsByLogisticsBillId(this.id));
    this.deliveryTimestamp = System.currentTimeMillis();
  }

  /**
   * 收货
   */
  public void collect() {
    if (this.logisticsState == null || !this.logisticsState.equals(UpLogisticsBillEnum.DRIVER_DELIVEED.getStatus()))
      throw new BusinessException("物流单据状态不是已发车，确认收货失败！");
    this.juStatement(1);

    this.logisticsState = UpLogisticsBillEnum.DRIVER_SIGNIN.getStatus();
    this.storageTimestamp = System.currentTimeMillis();
  }

  /**
   * 取消发货
   */
  public void cancel() {
    if (!this.orderType.equals(UpLogisticsBillEnum.ORDER_TYPE_SALE.getStatus())) {
      throw new BusinessException("非我方发货的物流单，取消发货失败！");
    }
    if (this.logisticsState != null &&
            !this.logisticsState.equals(UpLogisticsBillEnum.DRIVER_DELIVEED.getStatus()))
      throw new BusinessException("物流单据状态不是已发货状态，取消发货失败！");
    this.juStatement(0);

    this.logisticsState = UpLogisticsBillEnum.DRIVER_UNDELIVERY.getStatus();
    this.deliveryTimestamp = null;
  }

  /**
   * 取消收货
   */
  public void cancelReceipt() {
    if (!this.orderType.equals(UpLogisticsBillEnum.ORDER_TYPE_RETURN_SALE.getStatus())) {
      throw new BusinessException("非我方收货的物流单，取消收货失败！");
    }
    if (this.logisticsState != null &&
            !this.logisticsState.equals(UpLogisticsBillEnum.DRIVER_SIGNIN.getStatus()))
      throw new BusinessException("物流单据状态不是已收货状态，取消收货失败！");
    if (this.statementState.equals(LogisticsBillEnums.Statement.STATEMENT.getStatus()))
      throw new BusinessException("该物流单已最终确认，取消签收失败！");
    this.juStatement(1);

    this.logisticsState = UpLogisticsBillEnum.DRIVER_DELIVEED.getStatus();
    this.storageTimestamp = null;
  }

  /**
   * 删除
   */
  public void remove() {
    if (!this.orderType.equals(UpLogisticsBillEnum.ORDER_TYPE_SALE.getStatus())) {
      throw new BusinessException("非我方发货的物流单，取消发货失败！");
    }
    if (this.logisticsState != null &&
            !this.logisticsState.equals(UpLogisticsBillEnum.DRIVER_UNDELIVERY.getStatus()))
      throw new BusinessException("物流单据状态不是未发车状态，删除失败！");
  }

  /**
   * 判断订单是否已生成对账单
   *
   * @param type
   */
  private void juStatement(int type) {
    //0发货 1收货
    switch (type) {
      case 0:
        if (this.orderType.equals(UpLogisticsBillEnum.ORDER_TYPE_SALE.getStatus())) {
          UpSaleOrderReadRepository saleOrderReadRepository = (UpSaleOrderReadRepository) SpringContextUtils.getBean(UpSaleOrderReadRepository.class);
          UpSaleOrder saleOrder = saleOrderReadRepository.getById(this.orderId);
          if (saleOrder.getOrdState().equals(UpSaleOrderEnums.State.STATEMENT.getStatus())) {
            throw new BusinessException("销售单" + saleOrder.getCode() + "已生成对账单，无法再修改相关信息！");
          }
        }
        break;
      case 1:
        if (this.orderType.equals(UpLogisticsBillEnum.ORDER_TYPE_RETURN_SALE.getStatus())) {
          UpSaleReturnOrderReadRepository saleReturnOrderReadRepository = (UpSaleReturnOrderReadRepository) SpringContextUtils.getBean(UpSaleReturnOrderReadRepository.class);
          UpSaleReturnOrder saleReturnOrder = saleReturnOrderReadRepository.getById(this.orderId);
          if (saleReturnOrder.getReturnOrdState().equals(UpSaleReturnOrderEnums.State.STATEMENT.getStatus())) {
            throw new BusinessException("销售退货单" + saleReturnOrder.getReturnCode() + "已生成对账单，无法再修改相关信息！");
          }
        }
        break;
    }
  }
}
